﻿using System;
using System.ComponentModel;
using System.Linq.Expressions;
using AddressManager.Common.Event;
using AddressManager.Common.Extension;

namespace AddressManager.Common.ViewModel
{
    public interface IViewModel : INotifyPropertyChanged
    {

    }

    /// <summary>
    /// Base class for all ViewModels (MVVM).
    /// </summary>
    public class AbstractViewModel : IViewModel, IEventViewModel
    {
        protected AbstractViewModel()
        {
        }

        /// <summary>
        /// Sets a property value.
        /// If the new value is different from the old one, then a PropertyChanged event will be sent.
        /// </summary>
        /// <typeparam name="TViewModel">The view model's type</typeparam>
        /// <typeparam name="TProperty">Propety value type</typeparam>
        /// <param name="source">The instance for which the property shall be set, typically 'this'</param>
        /// <param name="propertyExpression">Denotes the property, like 'x => x.MyProperty'</param>
        /// <param name="backingField">Property backing filed, like 'ref _myProperty'</param>
        /// <param name="newValue">The new value given to the setter</param>
        protected bool SetProperty<TViewModel, TProperty>(
            TViewModel source,
            Expression<Func<TViewModel, TProperty>> propertyExpression,
            ref TProperty backingField, TProperty newValue) where TViewModel : AbstractViewModel
        {
            if (Equals(backingField, newValue) == false)
            {
                backingField = newValue;
                var propertyName = source.ExtractPropertyName(propertyExpression);
                RaisePropertyChanged(propertyName);
                return true;
            }
            return false;
        }

        /// <summary>
        /// Raises the PropertyChanged event.
        /// </summary>
        protected void RaisePropertyChanged<TViewModel, TProperty>(TViewModel source, Expression<Func<TViewModel, TProperty>> propertyExpression)
        {
            RaisePropertyChanged(source.ExtractPropertyName(propertyExpression));
        }

        /// <summary>
        /// Raises the PropertyChanged event.
        /// Ensure that 'info' corresponds to the property name!
        /// Better use the other overload, e.g. RaisePropertyChanged(x=>x.Property).
        /// Even better is this one: SetProperty(this, x=>x.Property, ref _property, value) !
        /// </summary>
        protected void RaisePropertyChanged(string info)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(info));
        }

        public event PropertyChangedEventHandler PropertyChanged = delegate { };

        #region events

        #region adding

        public event EventHandler EntityAdded;

        protected virtual void OnEntityAdded(string info)
        {
            if (EntityAdded != null)
            {
                EntityAdded(this, new AddressManagerEventArgs(info));
            }
        }

        #endregion

        #region error

        public event EventHandler ErrorRaised;

        protected virtual void OnErrorRaised(string info)
        {
            if (ErrorRaised != null)
            {
                ErrorRaised(this, new AddressManagerEventArgs(info));
            }
        }

        #endregion

        #region getting

        public event EventHandler GettingFinished;

        protected virtual void OnGettingFinished()
        {
            if (GettingFinished != null)
            {
                GettingFinished(this, EventArgs.Empty);
            }
        }

        #endregion

        #region map

        public event EventHandler PinSetted;

        protected virtual void OnPinSetted()
        {
            if (PinSetted != null)
            {
                PinSetted(this, EventArgs.Empty);
            }
        }

        #endregion

        #region modification

        public event EventHandler EntityModified;

        protected virtual void OnEntityModified(string info)
        {
            if (EntityModified != null)
            {
                EntityModified(this, new AddressManagerEventArgs(info));
            }
        }
        
        #endregion

        #region deletion

        public event EventHandler EntityDeleted;

        protected virtual void OnEntityDeleted(string info)
        {
            if (EntityDeleted != null)
            {
                EntityDeleted(this, new AddressManagerEventArgs(info));
            }
        }

        #endregion

        #endregion
    }
}